home *** CD-ROM | disk | FTP | other *** search
/ 10,000 Great Games / 10,000 Great Games.iso / Product / 66 / data1.cab / Source_Files / Src / Grav.cpp < prev    next >
C/C++ Source or Header  |  2000-01-16  |  9KB  |  469 lines

  1. /*    This file contains gravity effect routines
  2.  
  3.     Data format of gravity_map is:
  4.   
  5.     short 'width'
  6.     short 'height'
  7.     
  8.     height times: 
  9.       
  10.     { int 'offset to scanline from here' }
  11.         
  12.     height times:
  13.           
  14.     {
  15.         for a line containing nothing:
  16.  
  17.         { 
  18.             short 'width' 
  19.         }
  20.             
  21.         or for a line containing something:
  22.               
  23.         { 
  24.             short 'leading zeros'
  25.             short 'writes'
  26.             writes times: short 'offset of source pixel'
  27.         }
  28.     }
  29. */
  30.  
  31. #include "stdafx.h"
  32.  
  33. #include <math.h>
  34.  
  35. static short *radial_gravity(int maxr, double a, double t, double f(double r, double a, double t), int &n)
  36. {
  37.     short w = 2 * maxr - 1, *gravity_map = new short [2 + 3 * w + square(w)], *g = gravity_map;
  38.     
  39.     // Store size of gravity map
  40.     
  41.     *g++ = w;
  42.     *g++ = w;
  43.  
  44.     // Make room for table to store line offsets
  45.  
  46.     int *current_line = (int *)g;
  47.  
  48.     g = (short *)¤t_line[w];
  49.     
  50.     // Compute the gravity map
  51.     
  52.     for (int y = -maxr + 1; y < maxr; y++)
  53.     {
  54.         short lead_zeros = 0, writes = 0, *where_writes, trail_zeros = 0, non_zero = FALSE;
  55.         int offset;
  56.  
  57.         // Store location of line
  58.  
  59.         *current_line++ = (int)g - (int)current_line;
  60.  
  61.         // Create gravity table
  62.         
  63.         for (int x = -maxr + 1; x < maxr; x++)
  64.         {
  65.             double r = sqrt(square(x) + square(y));
  66.             
  67.             if (r == 0 || r > maxr - 1)
  68.                 offset = 0;
  69.             else
  70.             {
  71.                 // Compute displacement in x and y direction
  72.                 
  73.                 double v = f(r / maxr, a, t), gx = v * x / r, gy = v * y / r;
  74.                 
  75.                 // Round
  76.                 
  77.                 gx = gx < 0? gx - 0.5 : gx + 0.5;
  78.                 gy = gy < 0? gy - 0.5 : gy + 0.5;
  79.                 
  80.                 // Clip
  81.                 
  82.                 if (x + gx <= -maxr)
  83.                     gx = -maxr + 1 - x;
  84.                 else if (x + gx >= maxr)
  85.                     gx = maxr - 1 - x;
  86.                 
  87.                 if (y + gy <= -maxr)
  88.                     gy = -maxr + 1 - y;
  89.                 else if (y + gy >= maxr)
  90.                     gy = maxr - 1 - y;
  91.                 
  92.                 // Compute offset
  93.                 
  94.                 offset = (int)gx + (int)gy * w;
  95.  
  96.                 ASSERT(offset == (short)offset);
  97.             }
  98.             
  99.             if (offset == 0)
  100.             {
  101.                 // No change for this pixel
  102.                 
  103.                 if (non_zero)
  104.                 {
  105.                     // Non zero occured, increase trailing zeros
  106.                     
  107.                     trail_zeros++;
  108.                     
  109.                     // Store zero in case there are non zero offsets following
  110.                     
  111.                     *g++ = offset;
  112.                     writes++;
  113.                 }
  114.                 else
  115.                 {
  116.                     // Only zeros yet, increase number of zeros at start
  117.                     
  118.                     lead_zeros++;
  119.                 }
  120.             }
  121.             else if (!non_zero)
  122.             {
  123.                 // First non zero offset, store number of starting zeros
  124.                 
  125.                 *g++ = lead_zeros;
  126.                 
  127.                 // Make room for the number of bytes to write
  128.                 
  129.                 where_writes = g;
  130.                 g++;
  131.  
  132.                 // Store first non zero offset
  133.                 
  134.                 *g++ = offset;
  135.                 writes++;
  136.                 
  137.                 // Indicate that non zero was encountered
  138.                 
  139.                 non_zero = TRUE;
  140.             }
  141.             else
  142.             {
  143.                 // Offset is non zero and at least one non zero offset before,
  144.                 // reset trailing zeros
  145.                 
  146.                 trail_zeros = 0;
  147.                 
  148.                 // Store value
  149.                 
  150.                 *g++ = offset;
  151.                 writes++;
  152.             }
  153.         }        
  154.     
  155.         if (writes != 0)
  156.         {
  157.             // Store number of bytes to write
  158.             
  159.             *where_writes = writes - trail_zeros;
  160.     
  161.             // Remove trailing zeros from this line
  162.     
  163.             g -= trail_zeros;
  164.         }
  165.         else
  166.         {
  167.             // Indicate that nothing has to be written
  168.  
  169.             *g++ = w;
  170.         }        
  171.     }    
  172.     
  173.     // Store bytes written
  174.     
  175.     n = (int)g - (int)gravity_map;
  176.  
  177.     // Return buffer
  178.  
  179.     return gravity_map;
  180. }
  181.  
  182. static double f_ripple(double r, double a, double t)
  183. {
  184.     return a * (1 - r) * cos(4.5 * PI * r + 2 * PI * t);    
  185. }
  186.  
  187. static double f_sphere(double r, double a, double t)
  188. {
  189.     return -a * r / sqrt(1 - r * r);
  190. }
  191.  
  192. void create_gravity()
  193. {
  194.     short *g;
  195.     int n;
  196.  
  197.     // Create ripple effect
  198.     
  199.     for (int t = 0; t < 6; t++)
  200.     {
  201.         g = radial_gravity(150, 20, (double)t / 6, f_ripple, n);                
  202.         cData::add_to_file("Gravity.dat", g, n, construct("RIPPLE%d", t + 1));
  203.         delete g;
  204.     }    
  205.  
  206.     // Create spheres
  207.  
  208.     g = radial_gravity(40, 3, 0, f_sphere, n);
  209.     cData::add_to_file("Gravity.dat", g, n, "SPHERE40");
  210.     delete g;
  211.  
  212.     g = radial_gravity(60, 5, 0, f_sphere, n);
  213.     cData::add_to_file("Gravity.dat", g, n, "SPHERE60");
  214.     delete g;
  215.  
  216.     g = radial_gravity(80, 7, 0, f_sphere, n);
  217.     cData::add_to_file("Gravity.dat", g, n, "SPHERE80");
  218.     delete g;
  219.  
  220.     g = radial_gravity(100, 10, 0, f_sphere, n);
  221.     cData::add_to_file("Gravity.dat", g, n, "SPHERE100");
  222.     delete g;
  223. }
  224.  
  225. void gravity_blit(LPDIRECTDRAWSURFACE4 dest, RECT *destrect, int x, int y, short *gravity_map)
  226. {    
  227.     BYTE *surface_copy, *s, *d;
  228.     DDSURFACEDESC2 destsurf;        
  229.     int rw, rh, w, h, max_x, max_y;
  230.  
  231.     // Get size of rectangle
  232.  
  233.     rw = destrect->right - destrect->left;
  234.     rh = destrect->bottom - destrect->top;
  235.     
  236.     // Get size of image
  237.  
  238.     w = *gravity_map++;
  239.     h = *gravity_map++;
  240.  
  241.     ASSERT(rw >= w && rh >= h);
  242.     
  243.     // Check if there will be anything to blit
  244.  
  245.     if (x <= -w || x >= rw || y <= -h || y >= rh)
  246.         return;
  247.     
  248.     // Compute distance from x to right of rectangle with maximum w
  249.  
  250.     max_x = rw - x;
  251.  
  252.     if (max_x > w)
  253.         max_x = w;
  254.  
  255.     // Compute distance from y to bottom of rectangle with maximum h
  256.  
  257.     max_y = rh - y;
  258.  
  259.     if (max_y > h) 
  260.         max_y = h;
  261.  
  262.     // Allocate temporary space
  263.  
  264.     surface_copy = new BYTE [w * h];
  265.  
  266.     // Lock surface    
  267.         
  268.     destsurf.dwSize = sizeof(destsurf);
  269.     
  270.     while (!draw_ok(dest->Lock(destrect, &destsurf, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, 0)));
  271.  
  272.     // Fill surface_copy buffer entirely (if not with image data then fill with black)
  273.  
  274.     s = (BYTE *)destsurf.lpSurface + (x > 0? x : 0) + y * destsurf.lPitch;
  275.     d = surface_copy;
  276.  
  277.     for (int i = 0; i < h; i++)
  278.     {
  279.         if (i < -y || i >= max_y)
  280.         {
  281.             FillMemory(d, w, black);
  282.         }
  283.         else
  284.         {
  285.             if (x >= 0)
  286.             { 
  287.                 CopyMemory(d, s, max_x);
  288.  
  289.                 if (max_x < w)
  290.                     FillMemory(d + max_x, w - max_x, black);
  291.             }
  292.             else // if (x < 0)
  293.             {                
  294.                 FillMemory(d, -x, black);
  295.                 CopyMemory(d - x, s, w + x);
  296.             }            
  297.         }        
  298.         
  299.         s += destsurf.lPitch;
  300.         d += w;
  301.     }    
  302.     
  303.     // Setup source and destination pointer
  304.  
  305.     s = surface_copy + (x < 0? -x : 0) + (y < 0? -y * w : 0);
  306.     d = (BYTE *)destsurf.lpSurface + (x > 0? x : 0) + (y > 0? y * destsurf.lPitch : 0);
  307.     
  308.     // Go to the right offset value in the gravity map
  309.  
  310.     if (y < 0)
  311.         gravity_map = (short *)((int *)gravity_map - y);
  312.  
  313.     // Setup some values required in the assembly part
  314.     
  315.     int write_x_start = x < 0? -x : 0,
  316.         write_y = y < 0? h + y : max_y;    
  317.  
  318.     // Do the blit
  319.  
  320.     __asm
  321.     {
  322.         mov esi, gravity_map
  323.  
  324.         mov ebx, s    
  325.         mov edi, d                
  326.         
  327.         mov ecx, write_y
  328.     blit_loop_y:
  329.  
  330.         // Get start of this scanline in the gravity map
  331.  
  332.         push esi
  333.         add esi, [esi]
  334.  
  335.         // Get number of leading zeros
  336.  
  337.         lodsw        
  338.         cwde
  339.  
  340.         // Check leading zeros >= max_x
  341.  
  342.         cmp eax, max_x
  343.         jge line_empty
  344.  
  345.         // Store registers
  346.  
  347.         push ebx
  348.         push ecx
  349.         push edi
  350.         
  351.         // Get pixels to skip
  352.  
  353.         sub eax, write_x_start
  354.         jc skip_more_than_leading
  355.  
  356.         // Case 1: There are less pixels to skip than the number of leading zeros
  357.  
  358.         // Get the remaining number of pixels to be written in ecx
  359.  
  360.         mov ecx, max_x
  361.         sub ecx, write_x_start
  362.         sub ecx, eax
  363.         jbe line_empty2
  364.  
  365.         // Adjust pointers to first pixel that has to be written
  366.  
  367.         add ebx, eax
  368.         add edi, eax
  369.  
  370.         // Get number of pixels writable in this line
  371.  
  372.         lodsw
  373.         cwde
  374.         
  375.         // Take the least of the two counters
  376.  
  377.         cmp eax, ecx
  378.         jge blit_loop_x
  379.         mov ecx, eax
  380.         jmp blit_loop_x
  381.  
  382.     skip_more_than_leading:
  383.  
  384.         // Case 2: There are more pixels to skip than the number of leading zeros
  385.  
  386.         // Get the number of pixels that still have to be skipped in edx
  387.  
  388.         neg eax
  389.         mov edx, eax
  390.  
  391.         // Get number of pixels writable in this line
  392.  
  393.         lodsw
  394.         cwde
  395.  
  396.         // Get the remaining number of pixels to be written in ecx
  397.  
  398.         mov ecx, eax
  399.         sub ecx, edx
  400.         jbe line_empty2
  401.  
  402.         // Adjust the gravity map pointer
  403.  
  404.         shl edx, 1
  405.         add esi, edx
  406.                 
  407.         // Enter loop
  408.  
  409.     blit_loop_x:
  410.  
  411.         // Get offset
  412.  
  413.         lodsw
  414.         cwde
  415.  
  416.         // Get pixel from offset location and store it to the current location
  417.  
  418.         mov dl, [ebx + eax]
  419.         mov [edi], dl
  420.  
  421.         // Goto next pixel
  422.  
  423.         inc ebx
  424.         inc edi
  425.  
  426.         loop blit_loop_x
  427.  
  428.     line_empty2:
  429.  
  430.         // Restore registers
  431.  
  432.         pop edi
  433.         pop ecx
  434.         pop ebx
  435.  
  436.     line_empty:
  437.  
  438.         // Go to next line
  439.     
  440.         add ebx, w        
  441.         add edi, destsurf.lPitch
  442.     
  443.         pop esi
  444.         add esi, 4        
  445.  
  446.         loop blit_loop_y
  447.     }    
  448.         
  449.     // Unlock surface
  450.         
  451.     backbuffer->Unlock(destrect);
  452.  
  453.     // Release temporary buffer
  454.  
  455.     delete surface_copy;
  456. }
  457.  
  458. static void write_all_gravity(cDisplayable *l)
  459. {
  460.     if (!no_parallax && !inawin && !low_detail_level)
  461.         for (; l != 0; l = (cDisplayable *)l->next)
  462.             l->write_gravity();
  463. }
  464.  
  465. void write_gravity()
  466. {
  467.     write_all_gravity(weapons);
  468.     write_all_gravity(effects);
  469. }